#include "StdAfx.h"
#include "RunGSPrinter.h"
#include "PrinterTypeList.h"
#include <iostream>
#include <fstream>
#include <string>

#pragma warning(disable: 4996)
#include "Gdevdsp.h"

using namespace std;

/* New device has been opened */
/* Tell user to use another device */
int display_open(void *handle, void *device)
{
	TRACE(_T("display_open"));
	return 0;
}

int display_preclose(void *handle, void *device)
{
	TRACE(_T("display_preclose"));
	return 0;
}

int display_close(void *handle, void *device)
{
	TRACE(_T("display_close"));
	return 0;
}

int display_presize(void *handle, void *device, int width, int height, 
					int raster, unsigned int format)
{
	TRACE(_T("display_presize"));
	return 0;
}

int display_size(void *handle, void *device, int width, int height, 
				 int raster, unsigned int format, unsigned char *pimage)
{
	TRACE(_T("display_size"));
	return 0;
}

int display_sync(void *handle, void *device)
{
	TRACE(_T("display_open"));
	return 0;
}

int display_page(void *handle, void *device, int copies, int flush)
{
	TRACE(_T("display_page"));
	return 0;
}

int display_update(void *handle, void *device, 
				   int x, int y, int w, int h)
{
	TRACE(_T("display_update"));
	return 0;
}

int display_separation(void *handle, void *device, 
					   int comp_num, const char *name,
					   unsigned short c, unsigned short m,
					   unsigned short y, unsigned short k)
{
	TRACE(_T("display_separation"));
	return 0;
}


display_callback display = { 
	sizeof(display_callback),
	DISPLAY_VERSION_MAJOR,
	DISPLAY_VERSION_MINOR,
	display_open,
	display_preclose,
	display_close,
	display_presize,
	display_size,
	display_sync,
	display_page,
	display_update,
#ifdef DISPLAY_DEBUG_USE_ALLOC
	display_memalloc,	/* memalloc */
	display_memfree,	/* memfree */
#else
	NULL,	/* memalloc */
	NULL,	/* memfree */
#endif
	display_separation
};

/*********************************************************************/
/* stdio functions */

static int GSDLLCALL
gsdll_stdin(void *instance, char *buf, int len)
{
	return 0;
}

static int GSDLLCALL
gsdll_stdout(void *instance, const char *str, int len)
{
	fwrite(str, 1, len, stdout);
	fflush(stdout);
	if (len < 50)
	 TRACE(str);
	else
	 TRACE("the length is large than 50, cancel\r\n");
	return len;
}

static int GSDLLCALL
gsdll_stderr(void *instance, const char *str, int len)
{
	fwrite(str, 1, len, stderr);
	fflush(stderr);
	TRACE(str);
	return len;
}

void replace(char* s, char ch1, char ch2)
{
	int i=0;
	for (; s[i]!='\0';i++)
	{
		if (s[i]==ch1)
			s[i]=ch2;
	}
	return;
}

RunGSPrinter::RunGSPrinter(void)
{
	m_hGSDll32 = NULL;
}

RunGSPrinter::~RunGSPrinter(void)
{
	if (m_hGSDll32)
		FreeLibrary(m_hGSDll32);
}

BOOL RunGSPrinter::Initialize()
{
	m_hGSDll32 = LoadLibrary(_T("gsdll32.dll"));
	if (m_hGSDll32 == NULL)
	{
		TRACE(_T("Failed to loadlibrary gsdll32.dll\r\n"));
		return FALSE;
	}

	gsapi_new_instance = (PFN_gsapi_new_instance)GetProcAddress(m_hGSDll32, "gsapi_new_instance");
	gsapi_delete_instance = (PFN_gsapi_delete_instance)GetProcAddress(m_hGSDll32, "gsapi_delete_instance");
	gsapi_init_with_args= (PFN_gsapi_init_with_args)GetProcAddress(m_hGSDll32, "gsapi_init_with_args");
	gsapi_exit = (PFN_gsapi_exit)GetProcAddress(m_hGSDll32, "gsapi_exit");
	gsapi_set_display_callback = (PFN_gsapi_set_display_callback)GetProcAddress(m_hGSDll32, "gsapi_set_display_callback");
	gsapi_set_stdio = (PFN_gsapi_set_stdio)GetProcAddress(m_hGSDll32, "gsapi_set_stdio");

	if (!gsapi_new_instance || !gsapi_delete_instance || !gsapi_init_with_args || !gsapi_exit)
	{
		TRACE(_T("Failed to get ProcAddress, the dll is dirtied\r\n"));
		return FALSE;
	}
	HDC hdc = GetDC(NULL);	/* get hdc for desktop */
	int depth = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
	sprintf(ddpi, "-dDisplayResolution=%d", GetDeviceCaps(hdc, LOGPIXELSY));
	ReleaseDC(NULL, hdc);
	int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | 
		DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
	if (depth == 32)
		format = DISPLAY_COLORS_RGB | DISPLAY_UNUSED_LAST | 
		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
	else if (depth == 16)
		format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | 
		DISPLAY_DEPTH_16 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST |
		DISPLAY_NATIVE_555;
	else if (depth > 8)
		format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | 
		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
	else if (depth >= 8)
		format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | 
		DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
	else if (depth >= 4)
		format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | 
		DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
	sprintf(dformat, "-dDisplayFormat=%d", format);

	char currentPath[MAX_PATH];
	GetModuleFileNameA(NULL,currentPath,MAX_PATH);
	(strrchr(currentPath, '\\'))[1] = 0;   
	sprintf(m_sIncludeFile, "-I%slib;%sfonts", currentPath, currentPath);
	sprintf(m_sIjsServerFile, "-sIjsServer=%sIjsServer.exe", currentPath);
	sprintf(m_sProfileOut, "%sProfile\\", currentPath);
	replace(m_sProfileOut, '\\', '/');
	return TRUE;
}

/*
* Internal code for the .quit operator.
* The real quit code is an integer on the operand stack.
* gs_interpret returns this only for a .quit with a zero exit code.
*/
#define e_Quit (-101)
BOOL RunGSPrinter::PrinterFile(LPSTR lpFileName, LPSTR lpPrinter, int /*nWidth*/, int /*nHeight*/)
{
	const char * gsargv[10];
	int gsargc;
	if (GetPaperInfoFromFile(lpFileName) == FALSE)
		return FALSE;
	int i = 0;
	gsargv[i++] = "LongPaperPrinter";	/* actual value doesn't matter */
	gsargv[i++] = ddpi;
	gsargv[i++] = dformat;
	gsargv[i++] = m_sIncludeFile;
	gsargv[i++] = "-dNOPAUSE";
	gsargv[i++] = "-sDEVICE=mswinpr2";
	char sOutputFile[MAX_PATH] = "";
	sprintf(sOutputFile, "-sOutputFile=%%printer%%%s", lpPrinter);
	gsargv[i++] = sOutputFile;
	char sPageSize[32] = "";
	sprintf(sPageSize, "-g%dx%d", m_nWidth, m_nHeight);
	gsargv[i++] = sPageSize;
	char sFileName[MAX_PATH] = "";
	strcpy(sFileName, lpFileName);
	gsargv[i++] = sFileName;
	gsargv[i] = NULL;
	gsargc = i;
	for (i = 0; i < gsargc; i++)
	{
		TRACE(gsargv[i]);
		TRACE(" ");
	}
	TRACE("\r\n");

	void *minst;

	int code = gsapi_new_instance(&minst, NULL);
	if (code < 0)
	{
		TRACE(_T("Failed to gsapi_new_instance, the error is %d\r\n"), code);
		return FALSE;
	}
	gsapi_set_stdio(minst, gsdll_stdin, gsdll_stdout, gsdll_stderr);
	gsapi_set_display_callback(minst, &display);
	code = gsapi_init_with_args(minst, gsargc, (char**)gsargv);
	int code1 = gsapi_exit(minst);
	if ((code == 0) || (code == e_Quit))
		code = code1;

	gsapi_delete_instance(minst);

	if ((code == 0) || (code == e_Quit))
		return FALSE;
	return TRUE;
}

#define PAGE_BOUNDINGBOX "%%PageBoundingBox:"
#define PAGE_TITLE		"%%Title:"
#define PAGE_COPIES	"%%BeginNonPPDFeature: NumCopies"
BOOL RunGSPrinter::GetPaperInfoFromFile(LPSTR lpFileName)
{
// 	HANDLE hFile = CreateFileA(lpFileName,
// 		GENERIC_READ,          // open for reading
// 		FILE_SHARE_READ,       // share for reading
// 		NULL,                  // default security
// 		OPEN_EXISTING,         // existing file only
// 		FILE_ATTRIBUTE_NORMAL, // normal file
// 		NULL);                 // no attr. template
	BOOL bRet = FALSE;
	std::string line;
	std::string filename = lpFileName;
	std::ifstream file;
	file.open(filename.c_str(), std::ifstream::in);
	if (!file.good())
	{
//		TRACEA(("Can't open the file %s to get paper size\r\n"), lpFileName);
		return bRet;
	}

	size_t nLength = strlen(PAGE_BOUNDINGBOX);
	size_t titleLength = strlen(PAGE_TITLE);
	size_t nLengthCopies = strlen(PAGE_COPIES);
	BOOL bTitle = FALSE;
	BOOL bSize = FALSE;
	BOOL bCopies = FALSE;
	m_nCopies = 1;
	while (getline(file, line))
	{
		if (bTitle && bSize && bCopies)
		{
			break;
		}
		if (line.length() > titleLength  && line.compare(0, titleLength, PAGE_TITLE) == 0)
		{
			m_strFileName = line.substr(titleLength).c_str();
			m_strFileName.TrimLeft(_T(" "));
			TRACE(_T("The file name is %s\r\n"), m_strFileName);
			int idx = m_strFileName.ReverseFind(_T('\\'));
			m_strFileName = m_strFileName.Mid(idx);
			bTitle = TRUE;
			continue;
		}

		if (line.length() > nLength  && line.compare(0, nLength, PAGE_BOUNDINGBOX) == 0)
		{
			string subline = line.substr(nLength);
			int x, y, width, height;
			sscanf(subline.c_str(), " %d %d %d %d", &x, &y, &width, &height);
			m_nWidth = width;
			m_nHeight = height;
			bSize = TRUE;
			bRet = TRUE;
			TRACE(_T("The paper size is %d*%d\r\n"), width, height);
			continue;
		}
		if (line.length() > nLengthCopies  && line.compare(0, nLengthCopies, PAGE_COPIES) == 0)
		{
			string subline = line.substr(nLengthCopies);
			sscanf(subline.c_str(), " %d", &m_nCopies);
			TRACE(_T("The paper copies is %d\r\n"), m_nCopies);
			bCopies = TRUE;
			continue;
		}

	}
	return bRet;
}

static char cIjsParam[2048] = "";
BOOL RunGSPrinter::PrinterFileToIjs(LPSTR lpFileName, LPSTR lpPrinter, PRINTER_TYPE* pPNTType, LPSTR lpColorSpace, int /*nWidth*/, int /*nHeight*/)
{
	const char * gsargv[20];
	int gsargc;
	if (GetPaperInfoFromFile(lpFileName) == FALSE)
		return FALSE;
	int i = 0;
	gsargv[i++] = "LongPaperPrinter";	/* actual value doesn't matter */
	gsargv[i++] = m_sIncludeFile;
	gsargv[i++] = ddpi;
	gsargv[i++] = dformat;
	gsargv[i++] = "-dSAFER";
	gsargv[i++] = "-sDEVICE=ijs";
	gsargv[i++] = m_sIjsServerFile;
	gsargv[i++] = "-sProcessColorModel=DeviceCMYK";
	gsargv[i++] = "-dNOPLATFONTS";
	gsargv[i++] = "-dTextAlphaBits=4";
	gsargv[i++] = "-dGraphicsAlphaBits=1";
	char sManufacturer[64];
#ifdef UNICODE
	char csTemp[64];
	WideCharToMultiByte( CP_ACP, 0, pPNTType->manufacturer, -1, csTemp, MAX_PATH, NULL, NULL);
	sprintf(sManufacturer, "-sDeviceManufacturer=%s", csTemp);
#else
	sprintf(sManufacturer, "-sDeviceManufacturer=%s", pPNTType->manufacturer);
#endif
	gsargv[i++] = sManufacturer;
	char sModel[64];
#ifdef UNICODE
	WideCharToMultiByte( CP_ACP, 0, pPNTType->driver, -1, csTemp, MAX_PATH, NULL, NULL);
	sprintf(sModel, "-sDeviceModel=%s", csTemp);
#else
	sprintf(sModel, "-sDeviceModel=%s", pPNTType->driver);
#endif
	gsargv[i++] = sModel;
//	gsargv[i++] = m_sProfileOut;
 	char sPageSize[32] = "";
	sprintf(sPageSize, "-g%dx%d", m_nWidth, m_nHeight);
//	gsargv[i++] = sPageSize;
	int dpi = pPNTType->dpi;
	if (pPNTType->family.CompareNoCase(_T("escp2")) == 0)
		dpi = (dpi == 300) ? 360 : 720;
	char czProfileOut[MAX_PATH] = "";
	if (strlen(lpColorSpace) != 0)
		sprintf(czProfileOut, "%s%s", m_sProfileOut, lpColorSpace);
	sprintf(cIjsParam,
//		"-sIjsParams=Quality=%ddpi,PaperSize=%.3fx%.3f,TopLefts=0.000x0.000,DitherAlgorithm=Adaptive,InkType=,MediaType=Plain,CyanDensity=0.000,MagentaDensity=1.000,YellowDensity=1.000,BlackDensity=1.000,Brightness=0.400,Contrast=1.000,PrintingDirection=Bidirectional,InputSlot=standard,ImageType=TextGraphics,nCopies=1,CyanCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,MagentaCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,YellowCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,BlackCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,InkLimit=3.200,nPass=1,bPaperCut=0,nPrintCommand=2,PrinterDriver=%s,bLeftRightInvert=0,bUseSpool=1,sFile=%s,JobMode=Page,FullBleed=0,Density=1.600,Gamma=1.5,Saturation=0.9,UseGloss=0,AdjustDotsize=0", 
		"-sIjsParams=Quality=%ddpi,PaperSize=%.3fx%.3f,TopLefts=0.000x0.000,DitherAlgorithm=Adaptive,InkType=,MediaType=Photo,CyanDensity=1.000,MagentaDensity=1.000,YellowDensity=1.000,BlackDensity=1.000,Brightness=1.000,Contrast=1.000,PrintingDirection=Bidirectional,InputSlot=standard,ImageType=TextGraphics,nCopies=1,CyanCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,MagentaCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,YellowCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,BlackCurve=(6 1 0.0) 0.000 0.000 0.650 0.400 1.000 1.000,InkLimit=3.200,nPass=1,bPaperCut=0,nPrintCommand=2,PrinterDriver=%s,bLeftRightInvert=0,bUseSpool=1,sFile=%s,ProfileOut=%s,JobMode=Page,FullBleed=0,Density=1.500,UseGloss=0,AdjustDotsize=0", 
		dpi,
		m_nWidth/72.0, m_nHeight/72.0,
		lpPrinter,
		"flowerparter.ps",
		czProfileOut);//m_strFileName);
	gsargv[i++] = cIjsParam;
	gsargv[i++] = "-dNOPAUSE";

	char sFileName[MAX_PATH] = "";
	strcpy(sFileName, lpFileName);
	gsargv[i++] = sFileName;
	gsargv[i] = NULL;
	gsargc = i;

	void *minst;

	int code = gsapi_new_instance(&minst, NULL);
	if (code < 0)
	{
		TRACE(_T("Failed to gsapi_new_instance, the error is %d\r\n"), code);
		return FALSE;
	}
	gsapi_set_stdio(minst, gsdll_stdin, gsdll_stdout, gsdll_stderr);
	gsapi_set_display_callback(minst, &display);
	code = gsapi_init_with_args(minst, gsargc, (char**)gsargv);
	int code1 = gsapi_exit(minst);
	if ((code == 0) || (code == e_Quit))
		code = code1;

	gsapi_delete_instance(minst);

	if ((code == 0) || (code == e_Quit))
		return FALSE;
	return TRUE;
}